﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;

using ComponentFactory.Krypton.Toolkit;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace XQMSServer
{
    public partial class frmXQMSServer : ComponentFactory.Krypton.Toolkit.KryptonForm
    {

        public frmXQMSServer()
        {
            InitializeComponent();

            // Hook into the click events on the header buttons
            kryptonHeaderGroup1.ButtonSpecs[0].Click += new EventHandler(OnLeftRight);
            kryptonHeaderGroup3.ButtonSpecs[0].Click += new EventHandler(OnUpDown);
        }

        #region GUI Related
        private int _widthLeftRight;
        private int _heightUpDown;
        
        private void OnLeftRight(object sender, EventArgs e)
        {
            // (1) Suspend changes until all splitter properties have been updated
            kryptonSplitContainer1.SuspendLayout();   

           // (2) Is the left header group currently expanded?
           if (kryptonSplitContainer1.FixedPanel == FixedPanel.None)
           {
               // (3) Make the left panel fixed in size
               kryptonSplitContainer1.FixedPanel = FixedPanel.Panel1;
               kryptonSplitContainer1.IsSplitterFixed = true; 

               // (4) Remember the current height of the header group      
               _widthLeftRight = kryptonHeaderGroup1.Width;

               // (5) Find the new width to use for the header group
               int newWidth = kryptonHeaderGroup1.PreferredSize.Height;
               
               // (6) Make the header group fixed just as the new height
               kryptonSplitContainer1.Panel1MinSize = newWidth;
               kryptonSplitContainer1.SplitterDistance = newWidth;

               // (7) Change header to be vertical and button to near edge      
               kryptonHeaderGroup1.HeaderPositionPrimary = VisualOrientation.Right;      
               kryptonHeaderGroup1.ButtonSpecs[0].Edge = PaletteRelativeEdgeAlign.Near;   
           }   
           else   
           {
               // Make the bottom panel not-fixed in size anymore
               kryptonSplitContainer1.FixedPanel = FixedPanel.None;
               kryptonSplitContainer1.IsSplitterFixed = false;

               // Put back the minimize size to the original
               kryptonSplitContainer1.Panel1MinSize = 100;
              
               // Calculate the correct splitter we want to put back
               kryptonSplitContainer1.SplitterDistance = _widthLeftRight;
              
               // Change header to be horizontal and button to far edge
               kryptonHeaderGroup1.HeaderPositionPrimary = VisualOrientation.Top;
               kryptonHeaderGroup1.ButtonSpecs[0].Edge = PaletteRelativeEdgeAlign.Far;   
           
           }         

            kryptonSplitContainer1.ResumeLayout(); 
        }

        private void OnUpDown(object sender, EventArgs e)
        {
            // (1) Is the bottom right header group currently expanded?
            if (kryptonSplitContainer2.FixedPanel == FixedPanel.None)
            {
                // (2) Make the bottom panel fixed in size
                kryptonSplitContainer2.FixedPanel = FixedPanel.Panel2;
                kryptonSplitContainer2.IsSplitterFixed = true;

                // (3) Remember the current height of the header group
                _heightUpDown = kryptonHeaderGroup3.Height;

                // (4) Find the new height to use for the header group
                int newHeight = kryptonHeaderGroup3.PreferredSize.Height;

                // (5) Make the header group fixed just as the new height
                kryptonSplitContainer2.Panel2MinSize = newHeight;
                kryptonSplitContainer2.SplitterDistance = kryptonSplitContainer2.Height;
            }
            else
            {
                // Make the bottom panel not-fixed in size anymore
                kryptonSplitContainer2.FixedPanel = FixedPanel.None;
                kryptonSplitContainer2.IsSplitterFixed = false;

                // Put back the minimum size to the original
                kryptonSplitContainer2.Panel2MinSize = 100;

                // Calculate the correct splitter we want to put back
                kryptonSplitContainer2.SplitterDistance =
                   kryptonSplitContainer2.Height - _heightUpDown - kryptonSplitContainer2.SplitterWidth;
            }
        }
        #endregion GUI Related

        //XQMSServiceClient client = new XQMSServiceClient();

        #region Thread Related
        Thread t;

        private void StartNewThread()
        {
            t = new Thread(new ThreadStart(StartServer));
            t.Start();
        }
        
        delegate void SetLabelCallBack(string text);
        delegate void SetGridCallBack(string category, string text);
        delegate void SetQueInformationCallBack(string category, string text);


        //private void SetLabel(string text)
        //{
        //    if (this.lbxqueue.invokerequired)
        //    {
        //        setlabelcallback d = new setlabelcallback(setlabel);
        //        this.invoke(d, new object[] { text });
        //    }
        //    else
        //    {
        //        this.lbxqueue.items.add(text);
        //    }

        //}

        //private void ClearLog(string text)
        //{
        //    if (this.lbxQueue.InvokeRequired)
        //    {
        //        SetLabelCallBack d = new SetLabelCallBack(ClearLog);
        //        this.Invoke(d, new object[] { text });
        //    }
        //    else
        //    {
        //        this.lbxQueue.Items.Clear();
        //    }

        //}

        //private void SetLabelQue(string text)
        //{
        //    if (this.lblQueueNo.InvokeRequired)
        //    {
        //        SetLabelCallBack d = new SetLabelCallBack(SetLabelQue);
        //        this.Invoke(d, new object[] { text });
        //    }
        //    else
        //    {
        //        //text = "0000" + text;
        //        text = text.PadLeft(4, '0');

        //        this.lblQueueNo.Text = text;
        //        //this.SetLabel(text);
        //    }

        //}

        private void SetGridViewQue(string queCategory, string text)
        {
            if (this.DGVInformation.InvokeRequired)
            {
                SetGridCallBack d = new SetGridCallBack(SetGridViewQue);
                this.Invoke(d, new object[] { queCategory, text });
            }
            else
            {
                DataRow[] arrDR = DSMain.Tables["DTInformation"].Select("queCat = '" + queCategory + "'");
                DataRow dr = DSMain.Tables["DTInformation"].NewRow();
                
                string textResult = text.PadLeft(4, '0');
                if (arrDR.Length <= 0)
                {
                    dr["queCat"] = queCategory;

                    dr["queNo"] = textResult;
                }
                else
                {
                    dr["queCat"] = queCategory;


                    int queNo = Convert.ToInt16(arrDR[arrDR.Length - 1][1]) + 1;
                    int queNoResult = (queNo > Convert.ToInt16(text)) ? queNo : Convert.ToInt16(textResult);

                    dr["queNo"] = queNoResult.ToString().PadLeft(4, '0');
                }

                DSMain.Tables["DTInformation"].Rows.Add(dr);
            }            
        }
        #endregion Thread Related

        #region Client Server
        TcpListener server;

        private void StartServer()
        {
            //TcpListener server = null;
            try
            {
                // Set the TcpListener on port 8080.
                //Int32 port = 8080;
                Int32 port = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["ServerPort"]);
                //IPAddress localAddr = IPAddress.Parse("127.0.0.1");
                IPAddress ipAddress = Dns.GetHostEntry(System.Configuration.ConfigurationManager.AppSettings["ServerName"]).AddressList[0];

                // TcpListener server = new TcpListener(port);
                server = new TcpListener(ipAddress, port);

                // Start listening for client requests.
                server.Start();

                // Buffer for reading data
                Byte[] bytes = new Byte[256];
                String data = null;

                // Enter the listening loop.
                while (true)
                {
                    //this.SetLabel("Waiting for a connection... ");

                    // Perform a blocking call to accept requests.
                    // You could also user server.AcceptSocket() here.
                    TcpClient client = server.AcceptTcpClient();

                    if (client.Connected)
                    {
                        //this.SetLabel("Connected!");

                        data = null;

                        // Get a stream object for reading and writing
                        NetworkStream stream = client.GetStream();

                        int i;

                        // Loop to receive all the data sent by the client.
                        while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            // Translate data bytes to a ASCII string.
                            data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                            //this.SetLabel("Received: " + data);

                            data = ClientDataProcess(data);

                            // Process the data sent by the client.
                            //data = data.ToUpper();

                            byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

                            // Send back a response.
                            stream.Write(msg, 0, msg.Length);
                            //this.SetLabel("Sent: " + data);
                        }

                        // Shutdown and end connection
                        client.Close();
                        //}
                    }
                }
            }
            catch (SocketException e)
            {
                //this.SetLabel("SocketException: " + e);
                MessageBox.Show("SocketException: " + e.Message);
            }
            finally
            {
                // Stop listening for new clients.
                server.Stop();
            }
        }
        #endregion Client Server

        #region Queue Related
        private int queVar = 0;
        private Queue[] queueList;
        private Queue[] queuePrio;

        private void InitQueue()
        {
            string[] queCat = System.Configuration.ConfigurationManager.AppSettings["QueCat"].Split(';');
            queueList = new Queue[queCat.Count()]; // handle antrian normal
            queuePrio = new Queue[queCat.Count()];  // handle antrian priority
            string[] queCatStartNo = System.Configuration.ConfigurationManager.AppSettings["QueCatStartNo"].Split(';');

            int i = 0;
            for (i = 0; i < queCat.Count(); i++)
            {
                DataRow dr = DSMain.Tables["DTMain"].NewRow();
                dr["queCat"] = queCat[i];
                //dr["queCatNumber"] = "No Category";
                dr["queNo"] = "No Queue";
                //dr["queNextNo"] = "No Queue";
                dr["queCatStartNo"] = Convert.ToInt16(queCatStartNo[i]);
                dr["queCatNo"] = Convert.ToInt16(queCatStartNo[i]) - 1;
                DSMain.Tables["DTMain"].Rows.Add(dr);

                queueList[i] = new Queue();
                queuePrio[i] = new Queue();
            }
        }

        private void QueueView(Queue[] queue, short queIndex)
        {
            //this.bgWorker.RunWorkerAsync();
            //ClearLog("");

            //foreach (object que in queue[queIndex])
            //{
            //    SetLabel(que.ToString());
            //}

            queue[queIndex].TrimToSize();
        }
        
        #endregion Queue Related

        #region Data Processing
        private void UpdateQueue(string ClientQueCatCode, string ClientQueCatNo, string queNo)
        {
            for (int i = 0; i < DSMain.Tables["DTMain"].Rows.Count; i++)
            {
                if (DSMain.Tables["DTMain"].Rows[i]["queCat"].ToString() == ClientQueCatCode)
                    DSMain.Tables["DTMain"].Rows[i]["queNo"] = ClientQueCatCode + ": " + ClientQueCatNo + ", No: " + queNo.PadLeft(4, '0');
            }

            DeleteQueNoInformation(ClientQueCatCode, queNo.PadLeft(4, '0'));
        }

        private void DeleteQueNoInformation(string queCategory, string text)
        {
            if (this.DGVInformation.InvokeRequired)
            {
                SetQueInformationCallBack d = new SetQueInformationCallBack(DeleteQueNoInformation);
                this.Invoke(d, new object[] { queCategory, text });
            }
            else
            {
                DataRow[] drInformation = DSMain.Tables["DTInformation"].Select("queCat = '" + queCategory + "' AND queNo = '" + text.PadLeft(4, '0') + "'");
                for (int i = 0; i < DSMain.Tables["DTInformation"].Rows.Count; i++)
                {
                    if (DSMain.Tables["DTInformation"].Rows[i]["queCat"] == drInformation[0][0] && DSMain.Tables["DTInformation"].Rows[i]["queNo"] == drInformation[0][1])
                    {
                        DSMain.Tables["DTInformation"].Rows[i].Delete();
                        return;
                    }
                }
            }
        }

        private short GetQueueIndex(string queCatCode)
        {
            for (short i = 0; i < DSMain.Tables["DTMain"].Rows.Count; i++)
            {
                if (DSMain.Tables["DTMain"].Rows[i]["queCat"].ToString() == queCatCode)
                {
                    return i;
                }
            }

            return 0;
        }

        private string Dequeue(string ClientQueCatCode, string ClientQueCatNo, short queIndex)
        {
            if (queuePrio[queIndex].Count > 0)
            {
                string que = queuePrio[queIndex].Peek().ToString();
                //SetLabelQue(que);

                queuePrio[queIndex].Dequeue();

                QueueView(queuePrio, queIndex);
                UpdateQueue(ClientQueCatCode, ClientQueCatNo, que);

                return que;
            }
            else if (queueList[queIndex].Count > 0)
            {
                string que = queueList[queIndex].Peek().ToString();
                //SetLabelQue(que);

                queueList[queIndex].Dequeue();

                QueueView(queueList, queIndex);
                UpdateQueue(ClientQueCatCode, ClientQueCatNo, que);

                return que;
            }
            else
            {
                //SetLabelQue("No Queue");
                return "0";
            }
        }

        private string ClientDataProcess(string data)
        {
            string[] dataSplit = data.Split(' ');
            string cmdID = dataSplit[0];
            string ClientQueCatCode = "";
            string ClientQueCatNo = "";
            
            if (dataSplit.Count() > 1)
            {
                ClientQueCatCode = dataSplit[1];
                ClientQueCatNo = dataSplit[2];
            }

            short queIndex = GetQueueIndex(ClientQueCatCode);

            switch (cmdID)
            {
                case "+": //Enqueue
                    queVar = Convert.ToInt16(DSMain.Tables["DTMain"].Rows[queIndex]["queCatNo"]) + 1;
                    DSMain.Tables["DTMain"].Rows[queIndex]["queCatNo"] = queVar;
                    queueList[queIndex].Enqueue(queVar);
                    //SetLabelQue(queueList[queIndex].Peek().ToString());
                    //UpdateQueue(ClientQueCatCode, ClientQueCatNo, queueList.Peek().ToString());

                    SetGridViewQue(dataSplit[1], queueList[queIndex].Peek().ToString());
                    break;
                case "-": //Dequeue
                    return Dequeue(ClientQueCatCode, ClientQueCatNo, queIndex);
                case "call":
                    string ClientQueCallNo = dataSplit[3];
                    Array arrTemp = Array.CreateInstance(typeof(int), queueList[queIndex].Count);
                    //queueList[queIndex].CopyTo(arrTemp, 0);
                    
                    //ClearLog("");

                    if (queueList[queIndex].Contains(Convert.ToInt32(ClientQueCallNo)))
                    {
                        arrTemp.SetValue(Convert.ToInt32(ClientQueCallNo), 0);

                        short i = 1;
                        foreach (object que in queueList[queIndex])
                        {
                            if (que.ToString() != ClientQueCallNo)
                            {
                                arrTemp.SetValue(Convert.ToInt32(que), i);
                                i++;
                            }
                        }

                        queueList[queIndex].Clear();

                        foreach (Object obj in arrTemp)
                        {
                            queueList[queIndex].Enqueue(obj);
                        }

                        if (queueList[queIndex].Count > 0)
                        {
                            string que = queueList[queIndex].Peek().ToString();
                            //SetLabelQue(que);

                            //SetGridViewQue(dataSplit[1], que);

                            queueList[queIndex].Dequeue();

                            QueueView(queueList, queIndex);
                            UpdateQueue(ClientQueCatCode, ClientQueCatNo, que);

                            return que;
                        }
                        else
                        {
                            //SetLabelQue("No Queue");
                            return "0";
                        }
                    }
                    else
                    {
                        //SetLabelQue("No Queue");
                        return "0";
                    }
                case "QueCatReq":
                    string QueCat = "";
                    foreach (DataRow dr in DSMain.Tables["DTMain"].Rows)
                    {
                        QueCat += dr["queCat"] + ";";
                    }

                    return QueCat;
                    //break;
                case "assign":
                    Boolean dup = false;
                    ClientQueCatNo = Convert.ToInt16(ClientQueCatNo).ToString();

                    foreach (object que in queuePrio[queIndex])
                        if (que.ToString() == ClientQueCatNo)
                            dup = true;
                    
                    if (!dup) queuePrio[queIndex].Enqueue(ClientQueCatNo);

                    break;
                default:
                    //SetLabel(data);
                    //break;
                    return "Server " + System.Configuration.ConfigurationManager.AppSettings["ServerName"] + " Online";
            }

            QueueView(queueList, queIndex);
            QueueView(queuePrio, queIndex);

            return "0";
        }
        #endregion Data Processing

        #region Database
        private void SaveTransaction()
        {
            string connString = System.Configuration.ConfigurationManager.ConnectionStrings["XQMS"].ConnectionString;
            string sqlCommand = "";

            XQMSServer.Class.Database.ExecuteSqlTransaction(connString, sqlCommand);
        }
        #endregion Database

        #region Media Related
        private void playSound(string path)
        {
            System.Media.SoundPlayer player =
                new System.Media.SoundPlayer();
            player.SoundLocation = path;
            player.Load();
            player.Play();
        }

        private void ConfigureMediaPlayer()
        {
            wmpMain.uiMode = "none";
            wmpMain.stretchToFit = true;
            //wmpMain.URL = @"http://go.microsoft.com/fwlink/?LinkId=95772";
            wmpMain.URL = System.Environment.CurrentDirectory + "\\Eating_Android_Commercial.mp4";
            wmpMain.settings.setMode("loop", true);
        }
        #endregion Media Related

        #region System Related
        private void SystemStartUp()
        {
            InitQueue();
            ConfigureMediaPlayer();
        }

        private void SystemShutDown()
        {
            t.Abort();
            server.Stop();
            wmpMain.close();
        }
        #endregion System Related

        #region Windows Forms
        private void frmXQMSServer_Load(object sender, EventArgs e)
        {
            //MessageBox.Show(client.GetMessage("Mike Liu"));

            //queueList = new Queue(client.QueueGet());

            //QueueView(queueList);

            SystemStartUp();
        }

        private void frmXQMSServer_Shown(object sender, EventArgs e)
        {
            StartNewThread();
        }

        private void frmXQMSServer_Closed(object sender, EventArgs e)
        {
            SystemShutDown();
        }
        #endregion Windows Forms
    }
}
